package console;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ConsoleTable {

  public static enum Alignment {
    LEFT, CENTER, RIGHT
  }

  private final int columnCount;

  private List<String[]> rows;

  private String[] columnNames;

  private Alignment[] alignments;

  private int[] columnWidths;

  private boolean printLines;

  public ConsoleTable(int columnCount) {
    this.columnCount = columnCount;
    this.alignments = new Alignment[columnCount];
    this.columnWidths = new int[columnCount];
    Arrays.fill(alignments, Alignment.LEFT);
    rows = new ArrayList<String[]>();
    printLines = true;
  }

  public void setPrintLines(boolean value) {
    printLines = value;
  }

  public boolean isPrintLines() {
    return printLines;
  }

  public int getColumCount() {
    return columnCount;
  }

  public void setAlignment(int columnIndex, Alignment alignment) {
    this.alignments[columnIndex] = alignment;
  }

  public void setColumnNames(String... entries) {
    if (entries.length != columnCount) {
      throw new IllegalArgumentException("The table has " + columnCount
          + " columns, but there are " + entries.length + " column names: "
          + Arrays.toString(entries));
    }
    this.columnNames = entries;
    recalculateColumnWidths();
  }

  public void addRow(String... entries) {
    if (entries.length != columnCount) {
      throw new IllegalArgumentException("The table has " + columnCount
          + " columns, but the new row has " + entries.length + " entries: "
          + Arrays.toString(entries));
    }
    for (int i = 0; i < columnCount; i++) {
      columnWidths[i] = Math.max(columnWidths[i], length(entries[i]));
    }
    rows.add(entries);
  }

  public String toString() {
    StringBuilder sb = new StringBuilder();
    if (columnNames != null) {
      appendValue(sb, columnNames[0], columnWidths[0], Alignment.CENTER);
      for (int i = 1; i < columnCount; i++) {
        appendColumnSeparator(sb);
        appendValue(sb, columnNames[i], columnWidths[i], Alignment.CENTER);
      }
      sb.append('\n');
      if (printLines) {
        append(sb, '-', columnWidths[0]);
        for (int i = 1; i < columnCount; i++) {
          sb.append("-+-");
          append(sb, '-', columnWidths[i]);
        }
        sb.append('\n');
      }
    }
    for (String[] row : rows) {
      appendValue(sb, row[0], columnWidths[0], alignments[0]);
      for (int i = 1; i < columnCount; i++) {
        appendColumnSeparator(sb);
        appendValue(sb, row[i], columnWidths[i], alignments[i]);
      }
      sb.append('\n');
    }
    return sb.toString();
  }

  private void append(StringBuilder sb, char c, int count) {
    for (int i = 0; i < count; i++) {
      sb.append(c);
    }
  }

  private void appendValue(StringBuilder sb, String text, int columnWidth,
      Alignment alignment) {
    int length = length(text);
    int space = columnWidth - length;
    int leftSpace = 0;
    int rightSpace = 0;
    switch (alignment) {
    case LEFT:
      leftSpace = 0;
      rightSpace = space;
      break;
    case CENTER:
      leftSpace = space / 2;
      rightSpace = space - leftSpace;
      break;
    case RIGHT:
      leftSpace = space;
      rightSpace = 0;
      break;
    }
    append(sb, ' ', leftSpace);
    if (text != null) {
      sb.append(text);
    }
    append(sb, ' ', rightSpace);
  }

  public void recalculateColumnWidths() {
    Arrays.fill(columnWidths, 0);
    if (columnNames != null) {
      for (int i = 0; i < columnCount; i++) {
        columnWidths[i] = Math.max(columnWidths[i], length(columnNames[i]));
      }
    }
    for (String[] row : rows) {
      for (int i = 0; i < columnCount; i++) {
        columnWidths[i] = Math.max(columnWidths[i], length(row[i]));
      }
    }
  }

  private void appendColumnSeparator(StringBuilder sb) {
    if (printLines) {
      sb.append(" | ");
    } else{
      sb.append(' ');}
  }

  private int length(String text) {
    return text == null ? 0 : text.length();
  }
}